﻿#include "FbxLoader.h"
#include "fbxsdk.h"
#include "MeshData.h"
#include "BoneWeight.h"
#include "Common.h"
#include <memory>
#include <string>

namespace DYResources
{
	glm::mat4x4 TransferMat(FbxAMatrix fbxMat);

	SkinnedMeshPtr FbxLoader::LoadSkinnedMesh()
	{
		MeshData* meshData = ReadMeshData();
		SkeletonPtr skeleton = LoadSkeleton();
		SkinnedMeshPtr skinnedMesh = std::make_shared<SkinnedMesh>();
		skinnedMesh->SetMeshData(meshData);
		skinnedMesh->SetSkeleton(skeleton);

		FbxMesh* fbxMesh = mFbxMeshes[0];
		int vertexCount = meshData->vertexCnt;
		int cpCount = fbxMesh->GetControlPointsCount();
		BoneWeight* boneWeights = new BoneWeight[vertexCount];
		BoneWeight* cpBoneWeights = new BoneWeight[cpCount];

		FbxNode* node = fbxMesh->GetNode();
		const FbxVector4 lT = node->GetGeometricTranslation(FbxNode::eSourcePivot);
		const FbxVector4 lR = node->GetGeometricRotation(FbxNode::eSourcePivot);
		const FbxVector4 lS = node->GetGeometricScaling(FbxNode::eSourcePivot);
		FbxAMatrix meshMat(lT, lR, lS);

		// 处理蒙皮数据
		int deformerCount = fbxMesh->GetDeformerCount();
		for (int i = 0; i < deformerCount; i++)
		{
			FbxDeformer* deformer = fbxMesh->GetDeformer(i, FbxDeformer::EDeformerType::eSkin);
			if (deformer == nullptr) continue;

			FbxSkin* fbxSkin = (FbxSkin*)deformer;
			int lClusterCount = fbxSkin->GetClusterCount();
			for (int j = 0; j < lClusterCount; j++)
			{
				FbxCluster* lCluster = fbxSkin->GetCluster(j);
				FbxNode* fbxNode = lCluster->GetLink();
				std::string nodeName = fbxNode->GetName();
				Bone* bone = skeleton->GetBone(nodeName);
				int boneIdx = bone->GetIndex();

				// 设置骨骼的offset矩阵
				FbxAMatrix transMat, linkMat;
				lCluster->GetTransformMatrix(transMat);
				lCluster->GetTransformLinkMatrix(linkMat);
				FbxAMatrix combineMat = linkMat.Inverse() * transMat * meshMat;  // 矩阵使用左乘，列优先
				bone->mOffsetMat = TransferMat(combineMat);

				int cpIndicesCnt = lCluster->GetControlPointIndicesCount();
				int* cpIndices = lCluster->GetControlPointIndices();
				double* cpWeights = lCluster->GetControlPointWeights();
				for (int k = 0; k < cpIndicesCnt; k++)
				{
					int cpIndex = cpIndices[k];
					double cpWeight = cpWeights[k];
					cpBoneWeights[cpIndex].Set(boneIdx, cpWeight);
				}
			}
		}

		// 将control point index转换为顶点的索引
		int vertexIdx = 0;
		int numTriangles = fbxMesh->GetPolygonCount();
		for (int polygonIdx = 0; polygonIdx < numTriangles; polygonIdx++)
		{
			int lPolygonSize = fbxMesh->GetPolygonSize(polygonIdx);
			for (int i = 0; i < lPolygonSize; i++)
			{
				int controlPointIndex = fbxMesh->GetPolygonVertex(polygonIdx, i);
				boneWeights[vertexIdx] = cpBoneWeights[controlPointIndex];
				vertexIdx++;
			}
		}

		delete[] cpBoneWeights;

		for (int i = 0; i < vertexCount; i++)
		{
			auto boneWeight = boneWeights[i];
			float weight = 0;
			if (boneWeight.boneIndex1 != -1) 
				weight += boneWeight.boneWeight1;
			if (boneWeight.boneIndex2 != -1) 
				weight += boneWeight.boneWeight2;
			if (boneWeight.boneIndex3 != -1) 
				weight += boneWeight.boneWeight3;
			if (boneWeight.boneIndex4 != -1) 
				weight += boneWeight.boneWeight4;
			if (weight != 1)
			{
				Print("顶点权重有问题，不为 1 \n");
			}
		}

		skinnedMesh->SetVertexWeights(boneWeights);
		return skinnedMesh;
	}

	SkeletonPtr FbxLoader::LoadSkeleton()
	{
		int boneCount = mSkeletonNodes.size();
		SkeletonPtr skeleton = make_shared<Skeleton>();
		skeleton->CreateBoneArray(boneCount);
		for (int i = 0; i < boneCount; i++)
		{
			FbxNode* fbxNode = mSkeletonNodes[i];
			std::string nodeName = fbxNode->GetName();
			FbxDouble* translate = fbxNode->LclTranslation.Get().mData;
			FbxDouble* rotation = fbxNode->LclRotation.Get().mData;
			FbxDouble* scaling = fbxNode->LclScaling.Get().mData;

			Bone* bone = new Bone();
			bone->SetName(nodeName);
			bone->mLclTranslation = glm::vec3(translate[0], translate[1], translate[2]);
			bone->mLclRotation = glm::vec3(rotation[0], rotation[1], rotation[2]);
			bone->mLclScaling = glm::vec3(scaling[0], scaling[1], scaling[2]);
			skeleton->SetBone(i, bone);

			FbxNode* fbxParentNode = fbxNode->GetParent();
			if (fbxParentNode != nullptr)
			{
				std::string parentName = fbxParentNode->GetName();
				if (skeleton->IsBoneExist(parentName))
				{
					Bone* parentBone = skeleton->GetBone(parentName);
					bone->SetParent(parentBone);
					parentBone->AddChild(bone);
				}
			}
		}

		return skeleton;
	}

	glm::mat4x4 TransferMat(FbxAMatrix fbxMat)
	{
		FbxDouble4 m1 = fbxMat.mData[0];
		FbxDouble4 m2 = fbxMat.mData[1];
		FbxDouble4 m3 = fbxMat.mData[2];
		FbxDouble4 m4 = fbxMat.mData[3];

		glm::mat4x4 mat;
		mat[0][0] = m1[0]; mat[0][1] = m1[1]; mat[0][2] = m1[2]; mat[0][3] = m1[3];
		mat[1][0] = m2[0]; mat[1][1] = m2[1]; mat[1][2] = m2[2]; mat[1][3] = m2[3];
		mat[2][0] = m3[0]; mat[2][1] = m3[1]; mat[2][2] = m3[2]; mat[2][3] = m3[3];
		mat[3][0] = m4[0]; mat[3][1] = m4[1]; mat[3][2] = m4[2]; mat[3][3] = m4[3];
		return mat;
	}
}